﻿using Devil;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Profiling;

namespace GameToolkit
{

    public struct NativeBspGroup
    {
        public Bounds bounds;
        public int leftIndex;
        public int rightIndex;
        public int dataIndex;
        public int dataCount;
    }


    public class NativeBSP<T, TNative> : System.IDisposable where TNative : struct where T : class, INative<TNative>
    {

        internal struct NativeSpace
        {
            internal int parent;
            internal bool rightLeaf;
            internal BspBounds.SpaceNode space;
            internal static NativeSpace AsRoot(BspBounds.SpaceNode root)
            {
                NativeSpace sp;
                sp.parent = -1;
                sp.rightLeaf = false;
                sp.space = root;
                return sp;
            }
            internal static NativeSpace AsChild(int parent, BspBounds.SpaceNode node, bool rightChild)
            {
                NativeSpace sp;
                sp.parent = parent;
                sp.rightLeaf = rightChild;
                sp.space = node;
                return sp;
            }
        }

        int mGrpCount;
        int mDataCount;
        NativeArray<NativeBspGroup> nativeGroups;
        NativeArray<TNative> nativeDatas;
        Stack<NativeSpace> mStack;
        int mVersion;
        Allocator mAllocator;
        public int Version { get { return mVersion; } }
        public int NativeGroupCount { get { return mGrpCount; } }
        public NativeArray<NativeBspGroup> NativeGroupDatas { get { return nativeGroups; } }
        public int NativeDataCount { get { return mDataCount; } }
        public NativeArray<TNative> NativeDatas { get { return nativeDatas; } }
       

        FilterDelegate<T> mNoFilter;

        public NativeBSP(Allocator allocator = Allocator.Persistent)
        {
            //mStack = new Stack<NativeNode>();
            mAllocator = allocator;
            NativeArrayUtil.EnsureNativeSize(ref nativeGroups, 0, 128, allocator);
            NativeArrayUtil.EnsureNativeSize(ref nativeDatas, 0, 256, allocator);
            mNoFilter = (t) => true;
        }

        public void GetDatas(BspBounds bsp, FilterDelegate<T> filter = null) {

#if UNITY_EDITOR
            Profiler.BeginSample("Copy BSP Data");
#endif
            if (filter == null)
                filter = mNoFilter;
            mDataCount = 0;
            mGrpCount = 0;
            mVersion = bsp.Version;
            NativeArrayUtil.EnsureNativeSize(ref nativeGroups, 0, 128, mAllocator);
            NativeArrayUtil.EnsureNativeSize(ref nativeDatas, 0, 256, mAllocator);
            NativeSpace node = NativeSpace.AsRoot(bsp.RootSpace);
            if (node.space == null)
                return;
            if (mStack == null)
                mStack = new Stack<NativeSpace>(bsp.Deep << 1);
            else
                mStack.Clear();
            mStack.Push(node);
            while (mStack.Count > 0)
            {
                node = mStack.Pop();
                var grp = node.space;
                NativeArrayUtil.EnsureNativeSize(ref nativeGroups, mGrpCount, mGrpCount + 1, mAllocator);
                NativeBspGroup nativeGrp;
                nativeGrp.bounds = grp._bounds;
                nativeGrp.leftIndex = -1;
                nativeGrp.rightIndex = -1;
                nativeGrp.dataIndex = mDataCount;
                var data = grp.first;
                while (data != null)
                {
                    var dt = data.bounds.volume as T;
                    if (dt != null && filter(dt))
                    {
                        NativeArrayUtil.EnsureNativeSize(ref nativeDatas, mDataCount, mDataCount + 1, mAllocator);
                        nativeDatas[mDataCount++] = dt.GetNativeData();
                    }
                    data = data.next;
                }
                nativeGrp.dataCount = mDataCount - nativeGrp.dataIndex;
                nativeGroups[mGrpCount] = nativeGrp;
                if (node.parent != -1)
                {
                    var p = nativeGroups[node.parent];
                    if (node.rightLeaf)
                        p.rightIndex = mGrpCount;
                    else
                        p.leftIndex = mGrpCount;
                    nativeGroups[node.parent] = p;
                }
                if (grp.left != null)
                {
                    mStack.Push(NativeSpace.AsChild(mGrpCount, grp.left, false));
                }
                if (grp.right != null)
                {
                    mStack.Push(NativeSpace.AsChild(mGrpCount, grp.right, true));
                }
                mGrpCount++;
            }
#if UNITY_EDITOR
            Profiler.EndSample();
#endif
        }

        public void Dispose()
        {
            mDataCount = 0;
            mGrpCount = 0;
            if (nativeDatas.IsCreated)
                nativeDatas.Dispose();
            nativeDatas = default;
            if (nativeGroups.IsCreated)
                nativeGroups.Dispose();
            nativeGroups = default;
        }
    }
}